#include <iostream>
#include <functional>
#include <string>
#include <sys/epoll.h>
#include "Sock.hpp"
#include "epoll.hpp"

using namespace std;

class EpollServer
{
public:
    typedef function<void(string)> func_t;
    static const int gnum = 64;
    EpollServer(func_t handleRequest, const uint16_t port = 8080, const int revs_num = gnum) : _func(handleRequest), _port(port), _revs_num(revs_num)
    {
        // 1.创建套接字
        _listensock = Sock::Socket();
        Sock::Bind(_listensock, port);
        Sock::Listen(_listensock);
        logMessage(DEBUG, "init EpollServer success\n");
        // 2.创建epoll模型
        _epfd = Epoll::EpollCreate();
        logMessage(DEBUG, "create epoll success, epfd:%d\n", _epfd);
        // 3.注册监听套接字的读事件为epoll关心事件
        Epoll::EpollCtl(_epfd, EPOLL_CTL_ADD, _listensock, EPOLLIN);
        logMessage(DEBUG, "add listensock to epoll success!\n");
        // 4.wait之前需要有存放就绪事件的数组空间
        _revs = new struct epoll_event[_revs_num];
    }
    void loopOnce(int timeout) // 等待策略同select/poll
    {
        int n = Epoll::EpollWait(_epfd, _revs, _revs_num, timeout);
        // wait成功，将就绪事件放入数组内：
        // 假设：n个就绪，传入N个空间的数组
        // epoll按照顺序，放到数组[0,n-1]个空间中，只需遍历n，不需遍历整个数组

        // 1.如果数组太小，无法一次放完，就留等下次
        // 2.if(n==_revs_num)扩容...

        switch (n)
        {
        case 0: // 超时
            logMessage(DEBUG, "%s\n", "epoll timeout");
            break;
        case -1: // epoll出错
            logMessage(WARNING, "%s\n", "epoll error");
            break;
        default: // 获取到新连接/新请求
            handleEvent(n);
            break;
        }
    }
    void Start()
    {
        while (true)
        {
            int timeout = -1; // 等待策略：阻塞式
            loopOnce(timeout);
        }
    }
    ~EpollServer()
    {
        if (_listensock >= 0)
            close(_listensock);
        if (_epfd >= 0)
            close(_epfd);
        if (_revs)
            delete[] _revs;
    }

private:
    void handleEvent(int n) // 处理epollwait放在数组内的n个就绪事件:
    {
        for (int i = 0; i < n; i++)
        {
            int sock = _revs[i].data.fd;       // 哪个套接字
            uint32_t events = _revs[i].events; // 就绪事件
            if (events & EPOLLIN)              // 可读
            {
                if (sock == _listensock) // 新连接
                    Accept();
                else // 新请求
                    Recver(sock);
            }
        }
    }
    void Accept()
    {
        string cli_ip;
        uint16_t cli_port;
        int sock = Sock::Accept(_listensock, &cli_ip, &cli_port); // 此时获取连接，就不会阻塞了，因为epoll已经等待过了
        logMessage(DEBUG, "get new connect[%s:%d]:%d", cli_ip.c_str(), cli_port, sock);

        // 注册该连接的读事件为关心事件
        Epoll::EpollCtl(_epfd, EPOLL_CTL_ADD, sock, EPOLLIN);
    }
    void Recver(int sock)
    {
        char buffer[1024];
        ssize_t s = recv(sock, buffer, sizeof(buffer) - 1, 0);
        // 这样读取，不能保证读到一个完整报文，需要定制应用层协议
        if (s > 0)
        {
            buffer[s] = 0;
            _func(buffer); // 将（完整的）报文交给具体的业务逻辑
        }
        else if (s == 0) // 客户端关闭,断开连接:也是一种IO，也会被epoll检测到
        {
            // 从epoll模型中去掉该连接
            Epoll::EpollCtl(_epfd, EPOLL_CTL_DEL, sock, EPOLLIN); // 增删改都需要sock是合法的，先关闭再去掉容易报错
            // 关闭sock
            close(sock);
            logMessage(NORMAL, "client[%d] quit\n", sock);
        }
        else // 出错
        {
            Epoll::EpollCtl(_epfd, EPOLL_CTL_DEL, sock, EPOLLIN);
            close(sock);
            logMessage(WARNING, "recv error\n");
        }
    }

private:
    uint16_t _port;
    int _listensock;

    int _epfd;                 // epoll模型句柄
    struct epoll_event *_revs; // wait成功后返回一个就绪事件数组
    int _revs_num;             // 就绪事件数组大小

    func_t _func; // 绑定请求处理函数
};